﻿using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using mono = Mono.Cecil.Cil;

namespace MySpace.Common.Dynamic
{
	/// <summary>
	/// 	<para>Encapsulates methods for translating <see cref="OpCode"/> instances
	/// 	into <see cref="mono.OpCode"/> instances.</para>
	/// </summary>
	internal static class CecilTranslator
	{
		#region OpCodes

		private static readonly Dictionary<OpCode, mono.OpCode> _opCodes = new Dictionary<OpCode, mono.OpCode>
		{
			{ OpCodes.Nop, mono.OpCodes.Nop },
			{ OpCodes.Break, mono.OpCodes.Break },
			{ OpCodes.Ldarg_0, mono.OpCodes.Ldarg_0 },
			{ OpCodes.Ldarg_1, mono.OpCodes.Ldarg_1 },
			{ OpCodes.Ldarg_2, mono.OpCodes.Ldarg_2 },
			{ OpCodes.Ldarg_3, mono.OpCodes.Ldarg_3 },
			{ OpCodes.Ldloc_0, mono.OpCodes.Ldloc_0 },
			{ OpCodes.Ldloc_1, mono.OpCodes.Ldloc_1 },
			{ OpCodes.Ldloc_2, mono.OpCodes.Ldloc_2 },
			{ OpCodes.Ldloc_3, mono.OpCodes.Ldloc_3 },
			{ OpCodes.Stloc_0, mono.OpCodes.Stloc_0 },
			{ OpCodes.Stloc_1, mono.OpCodes.Stloc_1 },
			{ OpCodes.Stloc_2, mono.OpCodes.Stloc_2 },
			{ OpCodes.Stloc_3, mono.OpCodes.Stloc_3 },
			{ OpCodes.Ldarg_S, mono.OpCodes.Ldarg_S },
			{ OpCodes.Ldarga_S, mono.OpCodes.Ldarga_S },
			{ OpCodes.Starg_S, mono.OpCodes.Starg_S },
			{ OpCodes.Ldloc_S, mono.OpCodes.Ldloc_S },
			{ OpCodes.Ldloca_S, mono.OpCodes.Ldloca_S },
			{ OpCodes.Stloc_S, mono.OpCodes.Stloc_S },
			{ OpCodes.Ldnull, mono.OpCodes.Ldnull },
			{ OpCodes.Ldc_I4_M1, mono.OpCodes.Ldc_I4_M1 },
			{ OpCodes.Ldc_I4_0, mono.OpCodes.Ldc_I4_0 },
			{ OpCodes.Ldc_I4_1, mono.OpCodes.Ldc_I4_1 },
			{ OpCodes.Ldc_I4_2, mono.OpCodes.Ldc_I4_2 },
			{ OpCodes.Ldc_I4_3, mono.OpCodes.Ldc_I4_3 },
			{ OpCodes.Ldc_I4_4, mono.OpCodes.Ldc_I4_4 },
			{ OpCodes.Ldc_I4_5, mono.OpCodes.Ldc_I4_5 },
			{ OpCodes.Ldc_I4_6, mono.OpCodes.Ldc_I4_6 },
			{ OpCodes.Ldc_I4_7, mono.OpCodes.Ldc_I4_7 },
			{ OpCodes.Ldc_I4_8, mono.OpCodes.Ldc_I4_8 },
			{ OpCodes.Ldc_I4_S, mono.OpCodes.Ldc_I4_S },
			{ OpCodes.Ldc_I4, mono.OpCodes.Ldc_I4 },
			{ OpCodes.Ldc_I8, mono.OpCodes.Ldc_I8 },
			{ OpCodes.Ldc_R4, mono.OpCodes.Ldc_R4 },
			{ OpCodes.Ldc_R8, mono.OpCodes.Ldc_R8 },
			{ OpCodes.Dup, mono.OpCodes.Dup },
			{ OpCodes.Pop, mono.OpCodes.Pop },
			{ OpCodes.Jmp, mono.OpCodes.Jmp },
			{ OpCodes.Call, mono.OpCodes.Call },
			{ OpCodes.Calli, mono.OpCodes.Calli },
			{ OpCodes.Ret, mono.OpCodes.Ret },
			{ OpCodes.Br_S, mono.OpCodes.Br_S },
			{ OpCodes.Brfalse_S, mono.OpCodes.Brfalse_S },
			{ OpCodes.Brtrue_S, mono.OpCodes.Brtrue_S },
			{ OpCodes.Beq_S, mono.OpCodes.Beq_S },
			{ OpCodes.Bge_S, mono.OpCodes.Bge_S },
			{ OpCodes.Bgt_S, mono.OpCodes.Bgt_S },
			{ OpCodes.Ble_S, mono.OpCodes.Ble_S },
			{ OpCodes.Blt_S, mono.OpCodes.Blt_S },
			{ OpCodes.Bne_Un_S, mono.OpCodes.Bne_Un_S },
			{ OpCodes.Bge_Un_S, mono.OpCodes.Bge_Un_S },
			{ OpCodes.Bgt_Un_S, mono.OpCodes.Bgt_Un_S },
			{ OpCodes.Ble_Un_S, mono.OpCodes.Ble_Un_S },
			{ OpCodes.Blt_Un_S, mono.OpCodes.Blt_Un_S },
			{ OpCodes.Br, mono.OpCodes.Br },
			{ OpCodes.Brfalse, mono.OpCodes.Brfalse },
			{ OpCodes.Brtrue, mono.OpCodes.Brtrue },
			{ OpCodes.Beq, mono.OpCodes.Beq },
			{ OpCodes.Bge, mono.OpCodes.Bge },
			{ OpCodes.Bgt, mono.OpCodes.Bgt },
			{ OpCodes.Ble, mono.OpCodes.Ble },
			{ OpCodes.Blt, mono.OpCodes.Blt },
			{ OpCodes.Bne_Un, mono.OpCodes.Bne_Un },
			{ OpCodes.Bge_Un, mono.OpCodes.Bge_Un },
			{ OpCodes.Bgt_Un, mono.OpCodes.Bgt_Un },
			{ OpCodes.Ble_Un, mono.OpCodes.Ble_Un },
			{ OpCodes.Blt_Un, mono.OpCodes.Blt_Un },
			{ OpCodes.Switch, mono.OpCodes.Switch },
			{ OpCodes.Ldind_I1, mono.OpCodes.Ldind_I1 },
			{ OpCodes.Ldind_U1, mono.OpCodes.Ldind_U1 },
			{ OpCodes.Ldind_I2, mono.OpCodes.Ldind_I2 },
			{ OpCodes.Ldind_U2, mono.OpCodes.Ldind_U2 },
			{ OpCodes.Ldind_I4, mono.OpCodes.Ldind_I4 },
			{ OpCodes.Ldind_U4, mono.OpCodes.Ldind_U4 },
			{ OpCodes.Ldind_I8, mono.OpCodes.Ldind_I8 },
			{ OpCodes.Ldind_I, mono.OpCodes.Ldind_I },
			{ OpCodes.Ldind_R4, mono.OpCodes.Ldind_R4 },
			{ OpCodes.Ldind_R8, mono.OpCodes.Ldind_R8 },
			{ OpCodes.Ldind_Ref, mono.OpCodes.Ldind_Ref },
			{ OpCodes.Stind_Ref, mono.OpCodes.Stind_Ref },
			{ OpCodes.Stind_I1, mono.OpCodes.Stind_I1 },
			{ OpCodes.Stind_I2, mono.OpCodes.Stind_I2 },
			{ OpCodes.Stind_I4, mono.OpCodes.Stind_I4 },
			{ OpCodes.Stind_I8, mono.OpCodes.Stind_I8 },
			{ OpCodes.Stind_R4, mono.OpCodes.Stind_R4 },
			{ OpCodes.Stind_R8, mono.OpCodes.Stind_R8 },
			{ OpCodes.Add, mono.OpCodes.Add },
			{ OpCodes.Sub, mono.OpCodes.Sub },
			{ OpCodes.Mul, mono.OpCodes.Mul },
			{ OpCodes.Div, mono.OpCodes.Div },
			{ OpCodes.Div_Un, mono.OpCodes.Div_Un },
			{ OpCodes.Rem, mono.OpCodes.Rem },
			{ OpCodes.Rem_Un, mono.OpCodes.Rem_Un },
			{ OpCodes.And, mono.OpCodes.And },
			{ OpCodes.Or, mono.OpCodes.Or },
			{ OpCodes.Xor, mono.OpCodes.Xor },
			{ OpCodes.Shl, mono.OpCodes.Shl },
			{ OpCodes.Shr, mono.OpCodes.Shr },
			{ OpCodes.Shr_Un, mono.OpCodes.Shr_Un },
			{ OpCodes.Neg, mono.OpCodes.Neg },
			{ OpCodes.Not, mono.OpCodes.Not },
			{ OpCodes.Conv_I1, mono.OpCodes.Conv_I1 },
			{ OpCodes.Conv_I2, mono.OpCodes.Conv_I2 },
			{ OpCodes.Conv_I4, mono.OpCodes.Conv_I4 },
			{ OpCodes.Conv_I8, mono.OpCodes.Conv_I8 },
			{ OpCodes.Conv_R4, mono.OpCodes.Conv_R4 },
			{ OpCodes.Conv_R8, mono.OpCodes.Conv_R8 },
			{ OpCodes.Conv_U4, mono.OpCodes.Conv_U4 },
			{ OpCodes.Conv_U8, mono.OpCodes.Conv_U8 },
			{ OpCodes.Callvirt, mono.OpCodes.Callvirt },
			{ OpCodes.Cpobj, mono.OpCodes.Cpobj },
			{ OpCodes.Ldobj, mono.OpCodes.Ldobj },
			{ OpCodes.Ldstr, mono.OpCodes.Ldstr },
			{ OpCodes.Newobj, mono.OpCodes.Newobj },
			{ OpCodes.Castclass, mono.OpCodes.Castclass },
			{ OpCodes.Isinst, mono.OpCodes.Isinst },
			{ OpCodes.Conv_R_Un, mono.OpCodes.Conv_R_Un },
			{ OpCodes.Unbox, mono.OpCodes.Unbox },
			{ OpCodes.Throw, mono.OpCodes.Throw },
			{ OpCodes.Ldfld, mono.OpCodes.Ldfld },
			{ OpCodes.Ldflda, mono.OpCodes.Ldflda },
			{ OpCodes.Stfld, mono.OpCodes.Stfld },
			{ OpCodes.Ldsfld, mono.OpCodes.Ldsfld },
			{ OpCodes.Ldsflda, mono.OpCodes.Ldsflda },
			{ OpCodes.Stsfld, mono.OpCodes.Stsfld },
			{ OpCodes.Stobj, mono.OpCodes.Stobj },
			{ OpCodes.Conv_Ovf_I1_Un, mono.OpCodes.Conv_Ovf_I1_Un },
			{ OpCodes.Conv_Ovf_I2_Un, mono.OpCodes.Conv_Ovf_I2_Un },
			{ OpCodes.Conv_Ovf_I4_Un, mono.OpCodes.Conv_Ovf_I4_Un },
			{ OpCodes.Conv_Ovf_I8_Un, mono.OpCodes.Conv_Ovf_I8_Un },
			{ OpCodes.Conv_Ovf_U1_Un, mono.OpCodes.Conv_Ovf_U1_Un },
			{ OpCodes.Conv_Ovf_U2_Un, mono.OpCodes.Conv_Ovf_U2_Un },
			{ OpCodes.Conv_Ovf_U4_Un, mono.OpCodes.Conv_Ovf_U4_Un },
			{ OpCodes.Conv_Ovf_U8_Un, mono.OpCodes.Conv_Ovf_U8_Un },
			{ OpCodes.Conv_Ovf_I_Un, mono.OpCodes.Conv_Ovf_I_Un },
			{ OpCodes.Conv_Ovf_U_Un, mono.OpCodes.Conv_Ovf_U_Un },
			{ OpCodes.Box, mono.OpCodes.Box },
			{ OpCodes.Newarr, mono.OpCodes.Newarr },
			{ OpCodes.Ldlen, mono.OpCodes.Ldlen },
			{ OpCodes.Ldelem, mono.OpCodes.Ldelem_Any},
			{ OpCodes.Stelem, mono.OpCodes.Stelem_Any},
			{ OpCodes.Ldelema, mono.OpCodes.Ldelema },
			{ OpCodes.Ldelem_I1, mono.OpCodes.Ldelem_I1 },
			{ OpCodes.Ldelem_U1, mono.OpCodes.Ldelem_U1 },
			{ OpCodes.Ldelem_I2, mono.OpCodes.Ldelem_I2 },
			{ OpCodes.Ldelem_U2, mono.OpCodes.Ldelem_U2 },
			{ OpCodes.Ldelem_I4, mono.OpCodes.Ldelem_I4 },
			{ OpCodes.Ldelem_U4, mono.OpCodes.Ldelem_U4 },
			{ OpCodes.Ldelem_I8, mono.OpCodes.Ldelem_I8 },
			{ OpCodes.Ldelem_I, mono.OpCodes.Ldelem_I },
			{ OpCodes.Ldelem_R4, mono.OpCodes.Ldelem_R4 },
			{ OpCodes.Ldelem_R8, mono.OpCodes.Ldelem_R8 },
			{ OpCodes.Ldelem_Ref, mono.OpCodes.Ldelem_Ref },
			{ OpCodes.Stelem_I, mono.OpCodes.Stelem_I },
			{ OpCodes.Stelem_I1, mono.OpCodes.Stelem_I1 },
			{ OpCodes.Stelem_I2, mono.OpCodes.Stelem_I2 },
			{ OpCodes.Stelem_I4, mono.OpCodes.Stelem_I4 },
			{ OpCodes.Stelem_I8, mono.OpCodes.Stelem_I8 },
			{ OpCodes.Stelem_R4, mono.OpCodes.Stelem_R4 },
			{ OpCodes.Stelem_R8, mono.OpCodes.Stelem_R8 },
			{ OpCodes.Stelem_Ref, mono.OpCodes.Stelem_Ref },
			{ OpCodes.Unbox_Any, mono.OpCodes.Unbox_Any },
			{ OpCodes.Conv_Ovf_I1, mono.OpCodes.Conv_Ovf_I1 },
			{ OpCodes.Conv_Ovf_U1, mono.OpCodes.Conv_Ovf_U1 },
			{ OpCodes.Conv_Ovf_I2, mono.OpCodes.Conv_Ovf_I2 },
			{ OpCodes.Conv_Ovf_U2, mono.OpCodes.Conv_Ovf_U2 },
			{ OpCodes.Conv_Ovf_I4, mono.OpCodes.Conv_Ovf_I4 },
			{ OpCodes.Conv_Ovf_U4, mono.OpCodes.Conv_Ovf_U4 },
			{ OpCodes.Conv_Ovf_I8, mono.OpCodes.Conv_Ovf_I8 },
			{ OpCodes.Conv_Ovf_U8, mono.OpCodes.Conv_Ovf_U8 },
			{ OpCodes.Refanyval, mono.OpCodes.Refanyval },
			{ OpCodes.Ckfinite, mono.OpCodes.Ckfinite },
			{ OpCodes.Mkrefany, mono.OpCodes.Mkrefany },
			{ OpCodes.Ldtoken, mono.OpCodes.Ldtoken },
			{ OpCodes.Conv_U2, mono.OpCodes.Conv_U2 },
			{ OpCodes.Conv_U1, mono.OpCodes.Conv_U1 },
			{ OpCodes.Conv_I, mono.OpCodes.Conv_I },
			{ OpCodes.Conv_Ovf_I, mono.OpCodes.Conv_Ovf_I },
			{ OpCodes.Conv_Ovf_U, mono.OpCodes.Conv_Ovf_U },
			{ OpCodes.Add_Ovf, mono.OpCodes.Add_Ovf },
			{ OpCodes.Add_Ovf_Un, mono.OpCodes.Add_Ovf_Un },
			{ OpCodes.Mul_Ovf, mono.OpCodes.Mul_Ovf },
			{ OpCodes.Mul_Ovf_Un, mono.OpCodes.Mul_Ovf_Un },
			{ OpCodes.Sub_Ovf, mono.OpCodes.Sub_Ovf },
			{ OpCodes.Sub_Ovf_Un, mono.OpCodes.Sub_Ovf_Un },
			{ OpCodes.Endfinally, mono.OpCodes.Endfinally },
			{ OpCodes.Leave, mono.OpCodes.Leave },
			{ OpCodes.Leave_S, mono.OpCodes.Leave_S },
			{ OpCodes.Stind_I, mono.OpCodes.Stind_I },
			{ OpCodes.Conv_U, mono.OpCodes.Conv_U },
			{ OpCodes.Arglist, mono.OpCodes.Arglist },
			{ OpCodes.Ceq, mono.OpCodes.Ceq },
			{ OpCodes.Cgt, mono.OpCodes.Cgt },
			{ OpCodes.Cgt_Un, mono.OpCodes.Cgt_Un },
			{ OpCodes.Clt, mono.OpCodes.Clt },
			{ OpCodes.Clt_Un, mono.OpCodes.Clt_Un },
			{ OpCodes.Ldftn, mono.OpCodes.Ldftn },
			{ OpCodes.Ldvirtftn, mono.OpCodes.Ldvirtftn },
			{ OpCodes.Ldarg, mono.OpCodes.Ldarg },
			{ OpCodes.Ldarga, mono.OpCodes.Ldarga },
			{ OpCodes.Starg, mono.OpCodes.Starg },
			{ OpCodes.Ldloc, mono.OpCodes.Ldloc },
			{ OpCodes.Ldloca, mono.OpCodes.Ldloca },
			{ OpCodes.Stloc, mono.OpCodes.Stloc },
			{ OpCodes.Localloc, mono.OpCodes.Localloc },
			{ OpCodes.Endfilter, mono.OpCodes.Endfilter },
			{ OpCodes.Unaligned, mono.OpCodes.Unaligned },
			{ OpCodes.Volatile, mono.OpCodes.Volatile },
			{ OpCodes.Initobj, mono.OpCodes.Initobj },
			{ OpCodes.Constrained, mono.OpCodes.Constrained },
			{ OpCodes.Cpblk, mono.OpCodes.Cpblk },
			{ OpCodes.Initblk, mono.OpCodes.Initblk },
			{ OpCodes.Rethrow, mono.OpCodes.Rethrow },
			{ OpCodes.Sizeof, mono.OpCodes.Sizeof },
			{ OpCodes.Refanytype, mono.OpCodes.Refanytype },
			{ OpCodes.Readonly, mono.OpCodes.Readonly },
			{ OpCodes.Tailcall, mono.OpCodes.Tail }
		};

		#endregion

		/// <summary>
		/// 	<para>Translates the specified <see cref="OpCode"/> into an equivalent <see cref="mono.OpCode"/>.</para>
		/// </summary>
		/// <param name="opCode">The <see cref="OpCode"/> to translate.</param>
		/// <returns>
		///	<para>A <see cref="mono.OpCode"/> instance equivalent to the specified <see cref="OpCode"/> instance.</para>
		/// </returns>
		public static mono.OpCode Translate(OpCode opCode)
		{
			return _opCodes[opCode];
		}
	}
}
